﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;

namespace SharpZipDemo
{
    public static class Zip
    {
        // SharpZipLib 是一个免费的Zip操作类库，可以利用它对 ZIP 等多种格式进行压缩与解压。
        //下载网址：http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx
        //目前的版本为0.86。
        public static void GZipDecompress(string filepath)
        {
            //   解压  
            Stream s = new GZipInputStream(File.OpenRead(filepath));
            //生成一个GZipInputStream流，用来打开压缩文件。   
            //因为GZipInputStream由Stream派生，所以它可以赋给Stream。   
            //它的构造函数的参数是一个表示要解压的压缩文件所代表的文件流   
            FileStream fs = File.Create(Path.GetFileNameWithoutExtension(filepath));
            //生成一个文件流，它用来生成解压文件   
            //可以使用System.IO.File的静态函数Create来生成文件流   
            int size = 2048;//指定压缩块的大小，一般为2048的倍数   
            byte[] writeData = new byte[size];//指定缓冲区的大小   
            while (true)
            {
                size = s.Read(writeData, 0, size);//读入一个压缩块   
                if (size > 0)
                {
                    fs.Write(writeData, 0, size);//写入解压文件代表的文件流   
                }
                else
                {
                    break;//若读到压缩文件尾，则结束   
                }
            }
            s.Close();
        }

        public static void GZipCompress(string filepath)
        {
            //   压缩   
            Stream s = new GZipOutputStream(File.Create(filepath + ".zip "));
            //生成一个GZipOutputStream流，用来生成压缩文件。   
            //因为GZipOutputStream由Stream派生，所以它可以赋给Stream。   
            FileStream fs = File.OpenRead(filepath);
            //生成一个文件流，它用来打开要压缩的文件   
            //可以使用System.IO.File的静态函数OpenRead来生成文件流   
            byte[] writeData = new byte[fs.Length];
            //指定缓冲区的大小   
            fs.Read(writeData, 0, (int)fs.Length);
            //读入文件   
            s.Write(writeData, 0, writeData.Length);
            //写入压缩文件   
            s.Close();
            //关闭文件   
        }

        public static void Decompress(string filepath)
        {
            //   解压  
            BZip2.Decompress(File.OpenRead(filepath), File.Create(Path.GetFileNameWithoutExtension(filepath)), true);

        }

        public static void Compress(string filepath)
        {
            //   压缩   
            BZip2.Compress(File.OpenRead(filepath), File.Create(filepath + ".zip "), true, 4096);
        }

        public static string cutStr = "";


        /// <summary>
        /// 压缩文件
        /// 指定目录或者指定文件
        /// </summary>
        /// <param name="filespath">要压缩的文件路径</param>
        /// <param name="zipfilename">压缩后文件保存路径</param>
        /// <param name="fileFilter">对指定的文件进行压缩</param>
        /// <returns>bool</returns>
        public static bool ZipFile(string filespath, string zipfilename, string fileFilter)
        {
            try
            {
                //如果文件没有找到则报错。
                if (!File.Exists(filespath))
                {
                    throw new FileNotFoundException("指定的文件路径 " + filespath + " 无法找到该文件");
                }

                Crc32 crc32 = new Crc32();
                ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(zipfilename));
                zipOutputStream.SetLevel(6); // 设置压缩率 0~9 
                DirectoryInfo directoryInfo = new DirectoryInfo(filespath);
                FileInfo[] fileInfos = directoryInfo.GetFiles(fileFilter);
                cutStr = filespath.Trim();
                //压缩这个目录下的所有文件
                writeStream(ref zipOutputStream, fileInfos, crc32);
                //压缩这个目录下子目录及其文件
                direct(directoryInfo, ref zipOutputStream, crc32);

                zipOutputStream.Finish();
                zipOutputStream.Close();
            }
            catch
            {
                return false;
            }
            return true;
        }

        public static void ZipFile(string filespath, string zipfilename, int CompressionLevel, int BlockSize)
        {
            //如果文件没有找到则报错。
            if (!File.Exists(filespath))
            {
                throw new FileNotFoundException("指定的文件路径 " + filespath + " 无法找到该文件");
            }

            FileStream StreamToZip = new FileStream(filespath, FileMode.Open, FileAccess.Read);
            FileStream ZipFile = File.Create(zipfilename);
            ZipOutputStream ZipStream = new ZipOutputStream(ZipFile);
            ZipEntry ZipEntry = new ZipEntry("ZippedFile");
            ZipStream.PutNextEntry(ZipEntry);
            ZipStream.SetLevel(CompressionLevel);
            byte[] buffer = new byte[BlockSize];
            Int32 size = StreamToZip.Read(buffer, 0, buffer.Length);
            ZipStream.Write(buffer, 0, size);
            try
            {
                while (size < StreamToZip.Length)
                {
                    int sizeRead = StreamToZip.Read(buffer, 0, buffer.Length);
                    ZipStream.Write(buffer, 0, sizeRead);
                    size += sizeRead;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            ZipStream.Finish();
            ZipStream.Close();
            StreamToZip.Close();
        }

        //获取所有的目录信息
        private static void direct(DirectoryInfo di, ref ZipOutputStream s, Crc32 crc)
        {
            //DirectoryInfo di = new DirectoryInfo(filenames);
            DirectoryInfo[] dirs = di.GetDirectories("*");

            //遍历目录下面的所有的子目录
            foreach (DirectoryInfo dirNext in dirs)
            {
                //将该目录下的所有文件添加到 ZipOutputStream s 压缩流里面
                FileInfo[] a = dirNext.GetFiles();
                writeStream(ref s, a, crc);

                //递归调用直到把所有的目录遍历完成
                direct(dirNext, ref s, crc);
            }
        }

        private static void writeStream(ref ZipOutputStream s, FileInfo[] a, Crc32 crc)
        {
            foreach (FileInfo fi in a)
            {
                //string fifn = fi.FullName;
                FileStream fs = fi.OpenRead();

                byte[] buffer = new byte[fs.Length];
                fs.Read(buffer, 0, buffer.Length);


                //ZipEntry entry = new ZipEntry(file);    Path.GetFileName(file)
                string file = fi.FullName;
                file = file.Replace(cutStr, "");

                ZipEntry entry = new ZipEntry(file);

                entry.DateTime = DateTime.Now;

                // set Size and the crc, because the information
                // about the size and crc should be stored in the header
                // if it is not set it is automatically written in the footer.
                // (in this case size == crc == -1 in the header)
                // Some ZIP programs have problems with zip files that don't store
                // the size and crc in the header.
                entry.Size = fs.Length;
                fs.Close();

                crc.Reset();
                crc.Update(buffer);

                entry.Crc = crc.Value;

                s.PutNextEntry(entry);

                s.Write(buffer, 0, buffer.Length);
            }
        }

        /// <summary>
        /// 压缩指定目录下指定文件(包括子目录下的文件)
        /// </summary>
        /// <param name="zippath">args[0]为你要压缩的目录所在的路径 
        /// 例如：D:\\temp\\   (注意temp 后面加 \\ 但是你写程序的时候怎么修改都可以)</param>
        /// <param name="zipfilename">args[1]为压缩后的文件名及其路径
        /// 例如：D:\\temp.zip</param>
        /// <param name="fileFilter">文件过滤, 例如*.xml,这样只压缩.xml文件.</param>
        public static bool ZipFileMain(string zippath, string zipfilename, string fileFilter)
        {
            try
            {
                //string filenames = Directory.GetFiles(args[0]);

                Crc32 crc = new Crc32();
                ZipOutputStream s = new ZipOutputStream(File.Create(zipfilename));

                s.SetLevel(6); // 0 - store only to 9 - means best compression

                DirectoryInfo di = new DirectoryInfo(zippath);

                FileInfo[] a = di.GetFiles(fileFilter);

                cutStr = zippath.Trim();
                //压缩这个目录下的所有文件
                writeStream(ref s, a, crc);
                //压缩这个目录下子目录及其文件
                direct(di, ref s, crc);

                s.Finish();
                s.Close();
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 解压缩文件
        /// </summary>
        /// <param name="zipfilepath">待解压缩的文件路径</param>
        /// <param name="unzippath">解压缩到指定目录</param>
        public static void UnZip(string zipfilepath, string unzippath)
        {
            ZipInputStream zipInputStream = new ZipInputStream(File.OpenRead(zipfilepath));
            ZipEntry theEntry;
            while ((theEntry = zipInputStream.GetNextEntry()) != null)
            {
                string directoryName = Path.GetDirectoryName(unzippath);
                string fileName = Path.GetFileName(theEntry.Name);
                //生成解压目录
                Directory.CreateDirectory(directoryName);
                if (fileName != String.Empty)
                {
                    //如果文件的压缩后大小为0那么说明这个文件是空的,因此不需要进行读出写入
                    if (theEntry.CompressedSize == 0)
                        break;
                    //解压文件到指定的目录
                    directoryName = Path.GetDirectoryName(unzippath + theEntry.Name);
                    //建立下面的目录和子目录
                    Directory.CreateDirectory(directoryName);
                    FileStream streamWriter = File.Create(unzippath + theEntry.Name);
                    int size = 2048;
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        size = zipInputStream.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }
                    streamWriter.Close();
                }
            }
            zipInputStream.Close();
        }

        /// <summary>
        /// 创建zip文件，并添加文件：
        /// </summary>
        public static void Create()
        {
            using (ZipFile zip = ICSharpCode.SharpZipLib.Zip.ZipFile.Create(@"E:\test.zip"))
            {
                zip.BeginUpdate();
                zip.Add(@"E:\文件1.txt");
                zip.Add(@"E:\文件2.txt");
                zip.CommitUpdate();
            }
        }

        /// <summary>
        /// 将文件夹压缩为文件
        /// </summary>
        public static void CreateZip()
        {
            new FastZip().CreateZip(@"E:\test.zip", @"E:\test\", true, "");
        }

        //最后一个参数是使用正则表达式表示的过滤文件规则。
        //CreateZip方法有3个重载版本，其中有目录过滤参数、文件过滤参数及用于指定是否进行子目录递归的一个bool类型的参数。

        /// <summary>
        /// 将文件添加到已有zip文件中
        /// </summary>
        public static void AddZip()
        {
            using (ZipFile zip = new ZipFile(@"E:\test.zip"))
            {
                zip.BeginUpdate();
                zip.Add(@"E:\test.doc");
                zip.CommitUpdate();
            }
        }

        /// <summary>
        /// 列出zip文件中文件
        /// </summary>
        public static void ShowZipFile()
        {
            using (ZipFile zip = new ZipFile(@"E:\test.zip"))
            {
                string list = string.Empty;
                foreach (ZipEntry entry in zip)
                {
                    list += entry.Name + "\r\n";
                }
                MessageBox.Show(list);
            }
        }

        /// <summary>
        /// 删除zip文件中的一个文件
        /// </summary>
        public static void DeleteZipFile()
        {
            using (ZipFile zip = new ZipFile(@"E:\test.zip"))
            {
                zip.BeginUpdate();
                zip.Delete(@"test.doc");
                zip.Delete(@"test22.txt");
                zip.CommitUpdate();
            }
        }

        /// <summary>
        /// 解压zip文件中文件到指定目录下
        /// </summary>
        public static void ExtractZip()
        {
            (new FastZip()).ExtractZip(@"E:\test.zip", @"E:\test\", "");
        }



    }
}
